﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Data.SqlClient;

namespace MyLibrary.Data.DatabaseMonitor
{
    /// <summary>
    /// SQL Server数据库监控器
    /// </summary>
    public class SQLServerMonitor : IDatabaseMonitor, IDisposable
    {
        private string _connectionString;
        private int _interval;
        private string _monitorNmae;
        private string _tableName;
        private SqlConnection _sqlConnection = null;

        //监控线程定时器
        private Timer timer = null;
        //监控回调
        private TimerCallback timerCallback = null;

        private DateTime _startTime;    //这次监控开始的时间
        private DateTime _nextStartTime;    //下次监控开始的时间
        private TimeSpan _nextStartTimeSpan; //距离下次监控开始时间的间隔
        private int _lastRecordNumber = -1;  //最近一次检查数据库表时,记录的数量
        private MonitorStatus _status = MonitorStatus.NoStart; //监控器的状态
        private int _warmingTime = -1; //没有记录进入的警告次数
        private int _warmingCount = 0;  //警告计数器
        private bool _warmEnable = false; //是否报警,默认为不报警
        private string _logPath = String.Empty;

        public event DatabaseMonitorEventHandler AfterMonitor;
        /// <summary>
        /// SQL Server数据库监控器
        /// </summary>
        /// <param name="monitorName">数据库监控器名称</param>
        /// <param name="connectionString">连接字符串</param>
        /// <param name="interval">监控时间间隔</param>
        public SQLServerMonitor(string monitorName, string connectionString, int interval)
        {
            if (String.IsNullOrEmpty(monitorName)) throw new ArgumentNullException("监控器名称不能为空");
            if (String.IsNullOrEmpty(connectionString)) throw new ArgumentNullException("连接字符串不能为空");
            if (interval <= 0) throw new ArgumentException("监控间隔必须大于0");

            this._monitorNmae = monitorName;
            this._connectionString = connectionString;
            this._interval = interval;
        }

        ~SQLServerMonitor()
        {
            this.dispose(false);
        }

        /// <summary>
        /// 日志路径
        /// </summary>
        public string LogPath
        {
            get { return this._logPath; }
            set { this._logPath = value; }
        }
        /// <summary>
        /// 服务器IP地址
        /// </summary>
        public string ServerIP
        {
            get
            {
                if (this._sqlConnection != null)
                    return this._sqlConnection.DataSource;
                else
                    return String.Empty;
            }
        }

        /// <summary>
        /// 是否进行未有记录的报警
        /// </summary>
        public bool WarmEnable
        {
            get { return _warmEnable; }
            set 
            {
                if(value == false)
                    this._warmingCount = 0;
                _warmEnable = value; 
            }
        }

        /// <summary>
        /// 指定一个数字表示经过多少分钟数据表没有记录是发出警告
        /// </summary>
        public int WarmingSpan
        {
            get { return this._warmingTime * this._interval; }
            set
            {
                this._warmingTime = Convert.ToInt32(Math.Floor(Convert.ToDouble(value / this._interval)));
            }
        }
        
        /// <summary>
        /// 监控器监控的表
        /// </summary>
        public string TableName
        {
            get { return _tableName; }
            set { _tableName = value; }
        }
        /// <summary>
        /// 监控器状态
        /// </summary>
        public MonitorStatus Status
        {
            get { return _status; }
        }
        /// <summary>
        /// 数据库连接字符串
        /// </summary>
        public string DBConnectionStrng
        {
            get { return _connectionString; }
            set { _connectionString = value; }
        }

        /// <summary>
        /// 数据库监控的时间间隔
        /// </summary>
        public int Interval
        {
            get { return _interval; }
            set { _interval = value; }
        }

        /// <summary>
        /// 数据库监控器名称
        /// </summary>
        public string MonitorName
        {
            get { return _monitorNmae; }
        }

        /// <summary>
        /// 监控的表名
        /// </summary>
        public string MonitorTable
        {
            get { return _tableName; }
            set { _tableName = value; }
        }

        /// <summary>
        /// 打开数据库监视器
        /// </summary>
        /// <param name="monitorName">监控器的名称</param>
        public void Start()
        {
            //尝试建立SQL Server连接
            try
            {
                _sqlConnection = new SqlConnection();
                _sqlConnection.ConnectionString = this._connectionString;
            }
            catch(ArgumentException)
            {
                _sqlConnection.Dispose();
                _sqlConnection = null;
                throw new ArgumentException("数据库连接字符串有误");
            }

            //初始化变量
            this._nextStartTimeSpan = new TimeSpan(0, this._interval, 0);
            this._startTime = this._nextStartTime = DateTime.Now;
            //this._nextStartTime = this._startTime.Add(this._nextStartTimeSpan);
            //初始化定时器回调
            timerCallback = new TimerCallback(this.Monitor);
            //初始化定时器
            timer = new Timer(this.timerCallback, null, TimeSpan.Zero, this._nextStartTimeSpan);
            _status = MonitorStatus.Running;
        }
        /// <summary>
        /// 进行数据库监控,触发AfterMonitor事件
        /// </summary>
        public void Monitor(object objState)
        {
            DatabaseMonitorEventArgs dmea = new DatabaseMonitorEventArgs();
            dmea.MonitorName = this._monitorNmae;

            string sqlCommand = String.Empty;
            SqlCommand command = null;
            //如果监控表为空,则只需要知道数据库是否可用
            if (!String.IsNullOrEmpty(this._tableName))
                sqlCommand = "Select Count(1) From " + this._tableName;
            else
                sqlCommand = "Select Count(1) From sysobjects where xtype='U'";

            try
            {
                if (this._sqlConnection.State != System.Data.ConnectionState.Open)
                    this._sqlConnection.Open();
                command = new SqlCommand(sqlCommand, this._sqlConnection);
                int result = (int)command.ExecuteScalar();

                //增加的数量
                if (!String.IsNullOrEmpty(this._tableName))
                {
                    if (this._lastRecordNumber == -1)
                    {
                        dmea.RecordIncrease = (0 - result);
                        this._lastRecordNumber = result;
                        dmea.Result = MonitorResult.FirstReport;
                    }
                    else
                    {
                        dmea.RecordIncrease = (result - this._lastRecordNumber);
                        this._lastRecordNumber = result;
                        //需要报警
                        if (this._warmEnable)
                        {
                            //没有增加
                            if (dmea.RecordIncrease == 0)
                            {
                                _warmingCount++;
                                //当记录持续都没有变化时,发出报警
                                if (_warmingCount >= _warmingTime)
                                    dmea.Result = MonitorResult.Warming;
                                else
                                    dmea.Result = MonitorResult.Unchanging;
                            }
                            else
                            {
                                _warmingCount = 0;
                                dmea.Result = MonitorResult.Increase;
                            }
                        }
                        //不需要报警
                        else
                        {
                            if (dmea.RecordIncrease == 0)
                                dmea.Result = MonitorResult.Unchanging;
                            else
                                dmea.Result = MonitorResult.Increase;
                        }
                    }
                }
                else
                {
                    dmea.RecordIncrease = Int32.MaxValue;
                    dmea.Result = MonitorResult.Alive;
                }
            }
            catch (Exception)
            {
                //如果在执行期间出现错误
                dmea.RecordIncrease = Int32.MaxValue;
                dmea.Result = MonitorResult.Death;
            }
            finally
            {
                this._sqlConnection.Close();
                if(command != null)
                    command.Dispose();

                //如果时间间隔为Zero,说明此次的监控是在被停止后再继续的
                if (this._nextStartTimeSpan == TimeSpan.Zero)
                {
                    this._nextStartTimeSpan = new TimeSpan(0, this._interval, 0);
                    //改变计时器的行为
                    this.timer.Change(this._nextStartTimeSpan, this._nextStartTimeSpan);
                }
                this._startTime = this._nextStartTime;
                this._nextStartTime = this._nextStartTime.Add(this._nextStartTimeSpan);

                dmea.MonitorTime = this._startTime;
                dmea.NextMonitorTime = this._nextStartTime;

                //触发监控事件
                if (this.AfterMonitor != null)
                    this.AfterMonitor(dmea);
            }
        }
        /// <summary>
        /// 暂停监控
        /// </summary>
        public void Pause()
        {
            if (timer == null) throw new ArgumentNullException("监控定时器为空");

            //将定时器停止
            timer.Change(Timeout.Infinite, Timeout.Infinite);
            //计算如果再开定时器时,需要经历的时间
            this._nextStartTimeSpan = this._nextStartTime.Subtract(DateTime.Now);
            _status = MonitorStatus.Pause;
        }
        /// <summary>
        /// 继续监控
        /// </summary>
        public void Continue()
        {
            if (timer == null) throw new ArgumentNullException("监控定时器为空");

            //启动定时器,将计时器的执行时间间隔设置为上次监控被暂停的时间间隔
            timer.Change(this._nextStartTimeSpan, new TimeSpan(0, this._interval, 0));
            //更新下次执行时间,并将执行时间间隔设置为Zero
            this._nextStartTime = DateTime.Now.Add(this._nextStartTimeSpan);
            this._nextStartTimeSpan = TimeSpan.Zero;

            this._status = MonitorStatus.Running;
        }
        /// <summary>
        /// 关闭数据库监视器
        /// </summary>
        public void Stop()
        {
            if (this._status != MonitorStatus.Stop)
            {
                if (timer == null) throw new ArgumentNullException("监控定时器为空");
                //释放资源
                Dispose();
                this._lastRecordNumber = -1;
                this._status = MonitorStatus.Stop;
            }
        }

        #region IDisposable Members
        /// <summary>
        /// 释放资源
        /// </summary>
        public void Dispose()
        {
            dispose(true);
        }

        private void dispose(bool disposing)
        {
            if (disposing)
            {
                //释放资源
                if (this._sqlConnection != null)
                {
                    if (this._sqlConnection.State != System.Data.ConnectionState.Closed)
                        this._sqlConnection.Close();
                    this._sqlConnection.Dispose();
                    this._sqlConnection = null;
                }
                if (this.timer != null)
                {
                    //停止定时器
                    timer.Change(Timeout.Infinite, Timeout.Infinite);
                    this.timer.Dispose();
                    this.timer = null;
                }
            }
        }
        #endregion
    }
}
